#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <map>
#include <set>

#include "../LGraph/LGraph.h"
#include "../LGraph/Profile.h"

#define GRAPH_FILE "/wrk/stacc-sna/volodymyr/november/binary/lgraph_strong_symmetrical_2011_11.bin"

using namespace std;

const unsigned INTERVAL = 5184000; // 60 days

int main()
{

    LGraph *graph = LGraph::read_from_file(GRAPH_FILE);

    map<vertex_id_type, unsigned> A;
    map<vertex_id_type, map<vertex_id_type, unsigned> > Av2u;
    map<vertex_id_type, map<vertex_id_type, double> > credit;

    const string dir = "actions";
    const string actions[6] = {"action1.log", "action2.log", "action4.log", 
                               "action5.log", "action6.log", "action7.log"};
    for (int i = 0; i < 6; i++ )
    {
        string path = dir + '/' + actions[i];
        ifstream in(path.c_str());
        cout << path << endl;

        map<vertex_id_type, vertex_data_type> current_table;
        map<vertex_id_type, set<vertex_id_type> > neibs;

        // each tuple (user, action time)
        vertex_id_type u;
        vertex_data_type tu;
        while (in >> u >> tu)
        {
            vector<vertex_id_type> parents;
            A[u]++;

            // each possible parent from current_table
            for (map<vertex_id_type, vertex_data_type>::iterator it = current_table.begin(); 
                    it != current_table.end(); it++)
            {
                vertex_id_type v = it->first;
                vertex_data_type tv = it->second;
                if (neibs[v].count(u) > 0 && tu > tv && tu - tv < INTERVAL)
                {
                    if (Av2u.count(v) == 0)
                        Av2u[v] = map<vertex_id_type, unsigned>();
                    if (Av2u[v].count(u) == 0)
                        Av2u[v][u] = 0;
                    Av2u[v][u]++;
                        
                    parents.push_back(v);
                }
            }

            // update credit
            for (unsigned i = 0; i < parents.size(); i++)
            {
                vertex_id_type v = parents[i];

                if (credit.count(v) == 0)
                    credit[v] = map<vertex_id_type, double>();
                if (credit[v].count(u) == 0)
                    credit[v][u] = 0.0;
                credit[v][u] += 1.0 / parents.size();
            }

            // add to the table and save relevant edges
            current_table[u] = tu;
            set<vertex_id_type> nn;
            for (LGraph::iterator e = graph->iterate_adjacent_edges(u); !e.end(); e++)
                if ((*e).data <= tu)
                    nn.insert((*e).v2);
            neibs.insert(pair<vertex_id_type, set<vertex_id_type> >(u, nn));
        }
        in.close();
    }
    cout << "Training finished" << endl;

    map<vertex_id_type, double> pS;
    //compute influence scores
    for (map<vertex_id_type, map<vertex_id_type, double> >::iterator it = credit.begin(); it != credit.end(); it++)
    {
        vertex_id_type v = it->first;
        for (map<vertex_id_type, double>::iterator jt = (it->second).begin(); jt != (it->second).end(); jt++)
        {
            vertex_id_type u = jt->first;
            double p_vu = jt->second / double(A[v]);

            if (pS.count(u) == 0)
                pS[u] = p_vu;
            else
                pS[u] = pS[u] + (1.0 - pS[u])*p_vu;
        }
    }
    cout << "Scores computed" << endl;

    const string test_action = dir + '/' + string("action8.log");
    ifstream in(test_action.c_str());

    map<vertex_id_type, double> result_table;
    map<vertex_id_type, int> perform;
    //test data
    vertex_id_type v;
    vertex_data_type tv;
    while (in >> v >> tv)
    {
        if (result_table.count(v) > 0)
            perform[v] = 1;
        else
        {
            result_table[v] = 0.0;
            perform[v] = 2;
        }

        for (LGraph::iterator e = graph->iterate_adjacent_edges(v); !e.end(); e++)
        {
            if ((*e).data <= tv)
            {
                vertex_id_type u = (*e).v2;
                if (result_table.count(u) > 0)
                {
                    double pu = result_table[u];
                    if (credit.count(v) > 0 && credit[v].count(u) > 0)
                    {
                        double av = A.count(v) > 0 ? A[v] : 1.0;
                        pu = pu + (1.0 - pu)*(credit[v][u]/av);
                    }
                    result_table[u] = pu;
                }
                else if (pS.count(u) > 0)
                {
                    result_table[u] = pS[u];
                    perform[u] = 0;
                }
            }
        }
    }
    in.close();
    cout << "Test finished" << endl;

    ofstream res("results.discrete");
    //double thresh[10] = {0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95};
    double thresh = 0.0;
    while (thresh <= 1.0)
    {
        unsigned TP = 0, FN = 0, FP = 0, TN = 0;
        for (map<vertex_id_type, double>::iterator it = result_table.begin(); 
                it != result_table.end(); it++)
        {
            vertex_id_type u = it->first;
            double pu = it->second;

            if (perform[u] == 1 && pu >= thresh) TP++;
            if (perform[u] == 1 && pu <  thresh) FN++;
            if (perform[u] == 0 && pu >= thresh) FP++;
            if (perform[u] == 0 && pu <  thresh) TN++;
        }
        res << "T=" << thresh << ", TP=" << TP << ", FN=" << FN << ", FP=" << FP << ", TN=" << TN << endl;
        thresh += 0.01;
    }
    res.close();

    cout << "Done!" << endl;
    return 0;
}

